import Vue from 'vue'
import Vuex from 'vuex'
import { createLogger } from 'vuex'
import createPersistedState from "vuex-persistedstate";
import userInfo from './userInfo'

Vue.use(Vuex)
console.log("Vuex:",Vuex);
// Vuex.Store(options)
// Vuex.createLogger(ref)
// Vuex.createNamespacedHelpers(namespace)
// Vuex.install(_Vue)
// Vuex.mapActions(namespace, map)
// Vuex.mapGetters(namespace, map)
// Vuex.mapMutations(namespace, map)
// Vuex.mapState(namespace, map)
// Vuex.version

// 创建一个 store 实例。      VueX 4.x 中使用 createStore 来创建
// import { createStore } from 'vuex'
// const store = createStore({ ...options })

let store = new Vuex.Store({
  // state(){   //和 data 一样 重用 module 来说非常有用
  //   return {

  //   }
  // }
  state: {
    buttonPermission:{
      add:true,         //增加权限
      edit:false,       //编辑权限
      delete:false,     //删除劝降
      component:true,   //组件显示权限
    },
    buttonPermission2:["add", "edit", "component"],  //这种写法可以根据用户的登录的账号设置对应的权限数组，使用更普遍
    username:"张三",
    count:1,
    asyncCount:10,
  },
//==================================================================================================================================
//==================================================================================================================================

  getters: {
    // 第一种写法： 通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。
    setUsername (state, getters) {    //如果是子模块，还有rootState, rootGetters 两个参数
      // console.log(getters);
      return state.username + "属性"
    },

    // 第二种写法： 通过方法访问时，每次都会去进行调用，而不会缓存结果
    getTodoById: (state) => (string) => {
      return state.username + string
    }
  },
//==================================================================================================================================
//==================================================================================================================================

  mutations: {
    changeCount(state, payload){
      state.count = state.count + payload.param
    },
    asyncChangeCount(state, payload){
      state.asyncCount = state.asyncCount + payload.val
    }
  },
//==================================================================================================================================
//==================================================================================================================================

  actions: {
    // context上下文对象中包括：state getters rootState rootGetters commit dispath等属性方法，可解构出来 
    asyncChangeCount(context, payload){
      context.commit("asyncChangeCount",payload)
    },

    // 异步执行的 action
    asyncChangeCount2 ({ state, getters, rootState, rootGetters, commit, dispath }, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          commit('asyncChangeCount', payload)
          // console.log(state.asyncCount);
          resolve("异步执行的 action")
        }, 5000)
      })
    },

    // 在一个 action 中执行另一个 action     假设 getData() 和 getOtherData() 返回的是 Promise      下面的代码要注释掉
    actionB ({ dispatch, commit }) {
      console.log("actionB中的其他操作");
      return dispatch('actionA').then(() => {
        commit('someOtherMutation')
      })
    },
    // 利用 async / await，
    async actionA ({ commit }) {
      commit('gotData', await getData())
    },
    async actionB ({ dispatch, commit }) {
      await dispatch('actionA')                      // 等待 actionA 完成
      commit('gotOtherData', await getOtherData())
    }
  },
  
//==================================================================================================================================
//==================================================================================================================================

  modules: {
    userInfo
  },

//==================================================================================================================================
//==================================================================================================================================
  plugins:[
    //Vuex 自带一个日志插件用于一般的调试:
    // createLogger({  
    //   collapsed: false, // 自动展开记录的 mutation
    //   filter (mutation, stateBefore, stateAfter) {
    //     // 若 mutation 需要被记录，就让它返回 true 即可
    //     // 顺便，`mutation` 是个 { type, payload } 对象
    //     return mutation.type !== "aBlocklistedMutation"
    //   },
    //   actionFilter (action, state) {
    //     // 和 `filter` 一样，但是是针对 action 的
    //     // `action` 的格式是 `{ type, payload }`
    //     return action.type !== "aBlocklistedAction"
    //   },
    //   transformer (state) {
    //     // 在开始记录之前转换状态
    //     // 例如，只返回指定的子树
    //     return state.subTree
    //   },
    //   mutationTransformer (mutation) {
    //     // mutation 按照 { type, payload } 格式记录
    //     // 我们可以按任意方式格式化
    //     return mutation.type
    //   },
    //   actionTransformer (action) {
    //     // 和 `mutationTransformer` 一样，但是是针对 action 的
    //     return action.type
    //   },
    //   logActions: true, // 记录 action 日志
    //   logMutations: true, // 记录 mutation 日志
    //   logger: console, // 自定义 console 实现，默认为 `console`
    // }), 


    createPersistedState({            // localStorage持久化处理，默认是localStorage存储
// key ：存储持久状态的键。（默认：vuex）
// paths ：部分持续状态的任何路径的数组。如果没有路径给出，完整的状态是持久的。（默认：[]）
// reducer ：一个函数，将被调用来基于给定的路径持久化的状态。默认包含这些值。
// subscriber ：一个被调用来设置突变订阅的函数。默认为store=> handler=> store.subscribe(handler)
// storage ：而不是（或与）getState和setState。默认为localStorage。
// getState ：将被调用以重新水化先前持久状态的函数。默认使用storage。
// setState ：将被调用来保持给定状态的函数。默认使用storage。
// filter ：将被调用来过滤将setState最终触发存储的任何突变的函数。默认为() => true
        reducer(state) {
          return { ...state }   //全部保存在localstorage中
        }
    }), 

    createPersistedState({ // sessionStorage持久化处理
        storage: window.sessionStorage,
        key:"store",
        paths:["userInfo"],     //持久化 userInfo 子模块的数据
        reducer(state) {
          return {            //持久化存储的部分数据
            username: state.username,
            count: state.count,
            asyncCount: state.asyncCount,
            // moduleState: state.userInfo.moduleState    //存储子模块的数据
          }
        }
    }),

    // 自定义 VueX 插件
    // createWebSocketPlugin(socket)
  ],

  // 无论何时发生了状态变更且不是由 mutation函数引起的，将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
  // 注意点：不要在发布环境下启用严格模式！
  strict: false,
  // strict: process.env.NODE_ENV !== 'production'        // 让构建工具来关闭 发布环境 下的 严格模式，

  devtools: true,  // 是否将 Vuex 状态变更记录到控制台的 devtools 插件。默认为 true。
})

  // 这是 自定义 VueX 插件 的函数
  function createWebSocketPlugin (socket) {
    return (store) => {
      socket.on('data', data => {
        store.commit('receiveData', data)
      })
      store.subscribe(mutation => {
        if (mutation.type === 'UPDATE_DATA') {
          socket.emit('update', mutation.payload)
        }
      })
    }
  }    
  // 生成 state 的快照 的 插件函数： 生成状态快照的插件应该只在开发阶段使用，使用 webpack 或 Browserify
  const myPluginWithSnapshot = (store) => {
    let prevState = _.cloneDeep(store.state)
    store.subscribe((mutation, state) => {
      let nextState = _.cloneDeep(state)
      // 比较 prevState 和 nextState... 
      // 保存状态，用于下一次 mutation
      prevState = nextState
    })
  }
  

// ======================================================= store的静态成员 ========================================================


// ======================================================= store的实例成员 ========================================================
// ======================================================= store的实例成员 ========================================================
// ======================================================= store的实例成员 ========================================================

console.log(store);
// console.log(store.state);                        // 暴露出注册的 state。只读。
// console.log(store.getters);                      // 暴露出注册的 getter，只读。
// console.log(store.strict);                       // 是否在严格模式下运行，严格模式下任何 mutation 必须被记录

// store.commit(_type, _payload, _options)          // options 里可以有 root: true，它允许在命名空间模块里提交根的 mutation。

// store.dispatch(_type, _payload, _options)        // options 里可以有 root: true，它允许在命名空间模块里分发根的 action。

// store.replaceState(state)                        // 替换 store 的根状态，仅用状态合并或时光旅行调试。

// —————————————————————————————————————————————————— store.watch(getter, cb, options) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.watch(getter, cb, options) ——————————————————————————————————————————————————
// 调用此方法返回的函数即可停止侦听。

    // var stopWatch = store.watch(
    //   (state, getters) => state.count,                  // 监听一下这个数据，值发生改变时就调用回调函数
    //   (count) => console.log(count),
    //   { immediate: true }                               // 立即执行一次回调函数
    // )


// —————————————————————————————————————————————————— store.subscribe(fn, options) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.subscribe(fn, options) ——————————————————————————————————————————————————
// 订阅 store 的 mutation。在每个 mutation 完成后调用回调函数，接收 mutation 和经过 mutation 后的状态作为回调的参数：通常用于插件。

    // const unsubscribe = store.subscribe((mutation, state) => {    // unsubscribe 来停止订阅的函数。
    //   console.log(mutation.type)
    //   console.log(mutation.payload)
    // }, { 
    //   prepend: true }  // prepend: true 将回调函数添加到队列的前面，而不是最后
    // )


// —————————————————————————————————————————————————— store.subscribeAction(fn, options) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.subscribeAction(fn, options) ——————————————————————————————————————————————————
// 订阅 store 的 action。在每个 action 分发的时候调用回调，并接收 action 描述和当前的 store 的 state 这两个参数。通常用于插件。

    // const unsubscribe = store.subscribeAction((action, state) => {        // unsubscribe 来停止订阅的函数。
    //   console.log(action.type)
    //   console.log(action.payload)
    // }, { 
    //   prepend: true }  // prepend: true 将回调函数添加到队列的前面，而不是最后
    // )

    // store.subscribeAction({         // 指定订阅处理函数的被调用时机应该在一个 action 分发之前还是之后 (默认行为是之前)：
    //   before: (action, state) => {
    //     console.log(`before action ${action.type}`)
    //   },
    //   after: (action, state) => {
    //     console.log(`after action ${action.type}`)
    //   },
    //   error: (action, state, error) => {            // 捕获分发 action 的时候被抛出的错误。该函数会从第三个参数接收到一个 error 对象。
    //     console.log(`error action ${action.type}`)
    //     console.error(error)
    //   }
    // })


// —————————————————————————————————————————————————— store.hasModule(path) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.hasModule(path) ——————————————————————————————————————————————————
// 判断该模块的名字是否已经被注册


// —————————————————————————————————————————————————— store.registerModule(path: string , rawModule, options) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.registerModule(path: Array<string>, rawModule, options) ——————————————————————————————————————————————————
// 注册一个动态模块。   如果传入了数组，那么就是注册一个嵌套模块，后面的 元素项 是 前面元素项 的 子模块
// 应用场景： 按需加载  动态配置（根据角色不同）  插件系统  多租户应用（根据客户级别）  测试环境  条件性功能（如地理位置、设备类型等）

// 注册模块 `myModule`
    // store.registerModule('myModule', {}, { preserveState: true })          // preserveState: true  保留模块的 state
// 注册嵌套模块 `nested/myModule`
    // store.registerModule(['nested', 'myModule'], {})


// —————————————————————————————————————————————————— store.unregisterModule(path: string ) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.unregisterModule(path: Array<string>) ——————————————————————————————————————————————————
// 卸载一个动态模块。  注意：不能使用此方法卸载静态模块（即创建 store 时声明的模块）。


// —————————————————————————————————————————————————— store.hotUpdate(newOptions) ——————————————————————————————————————————————————
// —————————————————————————————————————————————————— store.hotUpdate(newOptions) ——————————————————————————————————————————————————
// 热替换新的 action 和 mutation。

    // if (module.hot) {     // 检查当前模块是否支持热更新。module.hot 是 Webpack 中的特性，判断模块是否能够进行热更新。

    //   // 使 action 和 mutation 成为可热重载模块
    //   module.hot.accept(['./mutations', './modules/a'], () => {   // 参数：需要监听变更的模块路径。监听的模块发生改变时的执行函数。 

    //     // 获取更新后的模块。因为 babel 6 的模块编译格式问题，这里需要加上 `.default`
    //     const newMutations = require('./mutations').default
    //     const newModuleA = require('./modules/a').default

    //     // 加载新模块
    //     store.hotUpdate({
    //       mutations: newMutations,
    //       modules: {
    //         a: newModuleA
    //       }
    //     })
    //   })
    // }

export default store